home *** CD-ROM | disk | FTP | other *** search
- #include <exec/types.h>
- #include <exec/nodes.h>
- #include <exec/resident.h>
- #include <exec/errors.h>
- #include <libraries/expansionbase.h>
- #include <libraries/configvars.h>
- #include <devices/trackdisk.h>
-
- /*
- * Set SysBase to a local variable, that loads directly from 4 when it
- * has to be reloaded
- */
- #define BASE_EXT_DECL
- #define BASE_NAME (*(void **)4)
- #include <inline/exec.h>
-
- /*
- * This is the main scsi-command executor task. It does the whole
- * scsi-communication and encapsulates hardware usage.
- */
-
- extern struct MsgPort *CreatePort(char *name, int pri);
- extern void DeletePort(struct MsgPort *mp);
-
- #include "device.h"
-
- DECL_DPRINTF;
- extern void install_int_handler (struct scsi_dev *dev);
- extern void remove_int_handler (struct scsi_dev *dev);
- extern void wait_interrupt (struct scsi_dev *dev);
- extern void clear_interrupt (struct scsi_dev *dev);
-
- void scsi_cmd (struct scsi_dev *dev);
- void init_board (struct scsi_dev *dev);
-
- /* this function is used by the int_handler, the rest is not */
- int read_scsi_reg (volatile ubyte *board, int reg_num);
- static void inline read_scsi_regs (volatile ubyte *board, int reg_start,
- ubyte num, ubyte *buf);
- static void inline write_scsi_regs (volatile ubyte *board, int reg_start,
- ubyte num, ubyte *buf);
- static void set_dma_state (volatile ubyte *board, int state, int value);
- static void write_scsi_reg (volatile ubyte *board, int reg_num, int value);
- static int poll_interrupts (struct scsi_dev *dev, int *status, int length);
- static void load_dma_address (volatile ubyte *board, int direction,
- void *address);
- static int get_cmd_len (int cmd);
- static int checkfifo_n_dmareset (volatile ubyte *board);
-
- /*
- * reset states to int-enable & scsi mode
- */
- #ifdef A2090
- #define REINIT_DMA_CIRCUIT() board[A2090_CNTL_HI] = MRESET|SEL_SCSI|INT_ENABLE
- #endif
- #ifdef A2091
- #define REINIT_DMA_CIRCUIT()
- #endif
-
- /***********************************************************************/
-
- /* this is the handler task, that gets command messages, executes them
- * and replies to them, when they are finished */
-
- void
- scsi_cmd (struct scsi_dev *dev)
- {
- struct scsi_msg *sm;
- struct MsgPort *mp;
- int i, cmd_len, status;
- volatile ubyte *board = (ubyte *)dev->sc_base;
- ulong length;
-
- DPRINTF(("handler-task: dev = $%lx, board = $%lx", dev, board));
-
- /* set up our interrupt handler */
- install_int_handler (dev);
-
- /* put the board into a defined state */
- init_board (dev);
-
- /* for now, just assume it worked... */
- mp = CreatePort("scsi_cmd.port",0);
- dev->sc_hmsgport = mp;
- PutMsg(&dev->sc_msgport, (struct Message *)&dev->sc_hmessage);
-
- for (;;)
- {
- WaitPort(mp);
- while (sm = (struct scsi_msg *) GetMsg(mp))
- {
- DPRINTF(("handler-task(%ld): cmd: %lx, scsi-cmd: %lx",
- sm->scm_unit, sm->scm_cmd, sm->scm_scsi_cmd->scsi_Command[0]));
-
- if (sm->scm_cmd == SCM_CMD_SHUTDOWN)
- goto shutdown;
- else if (sm->scm_cmd == SCM_CMD_EXEC_SCSI)
- {
- /*
- * first check, that we won't try to send a command
- * to the controller:
- */
- if (sm->scm_unit == 7)
- {
- sm->scm_cmd = HFERR_SelfUnit;
- ReplyMsg((struct Message*)sm);
- continue;
- }
-
- REINIT_DMA_CIRCUIT();
-
- /* since the user can specify an scsi_Length of up to
- * 2**24 byte, and the 2090-DMA circuit only stands
- * 64K, I'll limit any given Length to these 64K .. */
- length = sm->scm_scsi_cmd->scsi_Length;
- if (length > DMA_MAX_TRANSFER) length = DMA_MAX_TRANSFER;
-
- /* both, the 2090 and the 2091 can't handle more than
- * 24bit addresses as DMA target */
- if (length && (ulong)sm->scm_scsi_cmd->scsi_Data >= 0x01000000)
- AutoAutoRequest(SCSI_NAME,
- "Your DMA target is above $1000000",
- "This is not supported, this transfer will fail!",
- "Continue", "Continue");
-
- /* execute the SCSICommand attached to the message */
- if (length)
- load_dma_address (board,
- sm->scm_scsi_cmd->scsi_Flags & SCSIF_READ,
- sm->scm_scsi_cmd->scsi_Data);
-
- /*
- * copy the CDB (ignoring scsi_CmdLength... an SCSI command
- * has a fixed length, determined from its group code)
- */
- cmd_len = get_cmd_len (sm->scm_scsi_cmd->scsi_Command[0]);
- write_scsi_regs (board, WD_CDB_1, cmd_len,
- sm->scm_scsi_cmd->scsi_Command);
- sm->scm_scsi_cmd->scsi_CmdActual = cmd_len;
-
- /* set transfer length in WD chip, DMA is already ok */
- write_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
- ((ubyte *)&length)+1);
-
- write_scsi_reg (board, WD_DESTINATION_ID, sm->scm_unit);
-
- clear_interrupt (dev);
- write_scsi_reg (board, WD_COMMAND,
- WD_CMD_SELECT_WITHOUT_ATN_AND_TRANSFER);
-
- /*
- * this field serves two purposes, when the message is sent,
- * it is used as a global command, when the message is
- * returned, it contains the io_Error value
- */
- sm->scm_cmd = poll_interrupts (dev, &status, length);
-
- /*
- * get the value of the transfer counter after the execution
- * of the command
- */
- cmd_len = 0;
- read_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
- ((ubyte *)&cmd_len)+1);
-
- /* and deduce the actual transferred amount of data */
- sm->scm_scsi_cmd->scsi_Actual = length - cmd_len;
-
- sm->scm_scsi_cmd->scsi_Status = status;
- /*
- * if CHECK_CONDITION is set, and the user wanted to
- * AUTOSENSE, do a REQUEST_SENSE now
- */
- if ((status & 2) == 2 &&
- (sm->scm_scsi_cmd->scsi_Flags & SCSIF_AUTOSENSE))
- {
- DPRINTF(("handler-task(%ld): need autosense information", sm->scm_unit));
- load_dma_address (board,
- DIRECTION_READ,
- sm->scm_scsi_cmd->scsi_SenseData);
- /* REQUEST_SENSE */
- write_scsi_regs (board, WD_CDB_1, 4, (ubyte*)"\3\0\0\0");
- if (sm->scm_scsi_cmd->scsi_Flags & SCSIF_OLDAUTOSENSE)
- cmd_len = 4;
- else
- cmd_len = sm->scm_scsi_cmd->scsi_SenseLength;
- write_scsi_reg (board, WD_CDB_5,
- (sm->scm_scsi_cmd->scsi_Flags & SCSIF_OLDAUTOSENSE) ?
- 0 : cmd_len);
- write_scsi_reg (board, WD_CDB_6, 0);
-
- write_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
- ((ubyte *)&cmd_len)+1);
-
- /* needed the second time ?? */
- write_scsi_reg (board, WD_DESTINATION_ID, sm->scm_unit);
-
- clear_interrupt (dev);
- write_scsi_reg (board, WD_COMMAND,
- WD_CMD_SELECT_WITHOUT_ATN_AND_TRANSFER);
-
- poll_interrupts (dev, &status, cmd_len);
-
- cmd_len = 0;
- read_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
- ((ubyte *)&cmd_len)+1);
- sm->scm_scsi_cmd->scsi_SenseActual =
- sm->scm_scsi_cmd->scsi_SenseLength - cmd_len;
- DPRINTF(("\013handler-task(%ld): autosense information = $%lx",
- sm->scm_unit, *(ulong *)sm->scm_scsi_cmd->scsi_SenseData));
- }
- ReplyMsg ((struct Message*)sm);
- }
- }
- }
-
- shutdown :
- Forbid ();
- ReplyMsg ((struct Message *)sm);
- DeletePort (mp);
- remove_int_handler (dev);
- RemTask (FindTask (0));
- }
-
- /*
- * IMPORTANT: not all HFERR_ that could be detected are reported. This is
- * to try to ensure, that the bus is always in a defined state, when this
- * function returns. Only a timeout-condition on select and a disconnect
- * will cause this function to return.
- */
-
- int
- poll_interrupts (struct scsi_dev *dev, int *cmd_status, int length)
- {
- int status;
- int do_wait = 1; /* only needed after a command has been started */
- volatile ubyte *board = (volatile ubyte *)dev->sc_base;
-
- for (;;)
- {
- if (do_wait)
- {
- wait_interrupt (dev);
- do_wait = 0;
- }
-
- status = read_scsi_reg (board, WD_SCSI_STATUS);
-
- switch (status)
- {
- case 0x00: /* after RESET */
- case 0x16: /* COMMAND COMPLETED */
- break;
- case 0x1f: /* INFO OUT COMPLETED */
- do_wait = 1;
- clear_interrupt (dev);
- write_scsi_reg (board, WD_COMMAND,
- WD_CMD_TRANSFER_INFO|WD_CMD_SBT_MODE);
- while (! (read_scsi_reg (board, WD_AUXILIARY_STATUS) & 1)) ;
- read_scsi_reg (board, WD_DATA);
- break;
- case 0x20:
- do_wait = 1;
- clear_interrupt (dev);
- write_scsi_reg (board, WD_COMMAND, WD_CMD_NEGATE_ACK);
- break;
- case 0x42: /* SELECT TIMEOUT */
- if (length) checkfifo_n_dmareset (board);
- return HFERR_SelTimeout;
- case 0x4b: /* requested unusual info out */
- do_wait = 1;
- clear_interrupt (dev);
- write_scsi_reg (board, WD_COMMAND,
- WD_CMD_TRANSFER_INFO|WD_CMD_SBT_MODE);
- while (! (read_scsi_reg (board, WD_AUXILIARY_STATUS) & 1)) ;
- *cmd_status = read_scsi_reg (board, WD_DATA);
- /*
- * no (!) return here... the old 2090 device used to return
- * in such conditions, leaving a device in a connected state,
- * that would later timeout and leave the bus in a
- * broken state..
- */
- break;
- default:
- /* printf("strange-status: $%x\n", status); */
- break;
- case 0x85: /* DISCONNECT */
- if (!length || checkfifo_n_dmareset (board))
- {
- /* 0x60 == COMMAND_COMPLETED PHASE */
- if (read_scsi_reg (board, WD_COMMAND_PHASE) == 0x60)
- {
- *cmd_status = read_scsi_reg (board, WD_TARGET_LUN);
- return *cmd_status ? HFERR_BadStatus : 0;
- }
- else
- return HFERR_Phase;
- }
- else
- return HFERR_DMA;
- }
- }
- }
-
- void
- init_board (struct scsi_dev *dev)
- {
- volatile ubyte *board = (volatile ubyte *)dev->sc_base;
-
- #ifdef A2090
- /* ST506 init stuff... shudder.. */
-
- int i;
- int try;
- ulong foo_addr = 0x79000; /* aehm... */
-
- /* from 2620 monitor.. hope it works:-)) */
- for (i = 0; i < 2; i++)
- {
- try = 2500000;
- DPRINTF(("st506: 1part"))
- *(unsigned short *)&board[0x50] = foo_addr >> (1 + (i==1?8:0));
- board[A2090_CNTL_HI] = MRESET|HCBP; /* 506 stuff... */
- while (!(board[A2090_CNTL_HI] & (1<<7)) && try-- > 0) ;
- try = 2500000;
- DPRINTF(("st506: 2part"))
- board[A2090_CNTL_HI] = MRESET;
- while ((board[A2090_CNTL_HI] & (1<<7)) && try-- > 0) ;
- }
-
- board[A2090_CNTL_HI] = MRESET|SEL_SCSI;
- #endif
- #ifdef A3000
- board[0x0b] = 0;
- board[0x3f] = 0;
- board[0x03] = 0;
- board[0x1b] = 0;
- board[0x0b] = 12; /* what this might do..... */
- #endif
-
- read_scsi_reg (board, WD_SCSI_STATUS);
-
- REINIT_DMA_CIRCUIT();
-
- write_scsi_reg (board, WD_OWN_ID, 7);
- write_scsi_reg (board, WD_COMMAND, WD_CMD_RESET);
-
- DPRINTF(("init: wait_interrupt"))
- wait_interrupt (dev);
-
- DPRINTF(("init: set controller into dma mode"))
- do
- write_scsi_reg (board, WD_CONTROL, 0x80); /* set chip into DMA mode */
- while (read_scsi_reg (board, WD_CONTROL) != 0x80);
-
- /* write_scsi_reg (board, WD_TIMEOUT_PERIOD, 0xff); /* timeout ~~2s */
- write_scsi_reg (board, WD_TIMEOUT_PERIOD, 0x20); /* timeout minimal */
-
- read_scsi_reg (board, WD_SCSI_STATUS);
-
- write_scsi_reg (board, WD_SYNCHRONOUS_TRANSFER, 0x40); /* no sync */
-
- #ifdef A2090
- checkfifo_n_dmareset (board);
- #endif
- }
-
- /***********************************************************************/
-
- static int
- get_cmd_len (int cmd)
- {
- switch (cmd >> 5)
- {
- case 0: return 6;
- case 1: return 10;
- default: /* assume maximum */
- case 5: return 12;
- }
- }
-
- static void
- load_dma_address (volatile ubyte *board, int direction, void *address)
- {
- #ifdef A2090
- ulong data;
-
- data = (ulong)address >> 1;
- if (direction == DIRECTION_WRITE) data |= 0x800000;
- set_dma_state (board, DMA_LD_UP_ADDR, data >> 16);
- set_dma_state (board, DMA_LD_MID_ADDR, data >> 8);
- set_dma_state (board, DMA_LD_LO_ADDR, data);
- board[SCSI_PCSS] = DMA_OPEN_FIFO;
- #endif
- #ifdef A2091
- if (direction == DIRECTION_WRITE)
- *(ushort *)&board[0x42] = 0x38;
- else
- *(ushort *)&board[0x42] = 0x30;
-
- *(ulong *)&board[0x80] = 0;
- *(ulong *)&board[0x84] = (ulong) address;
- *(ushort *)&board[0xe0] = 1;
- #endif
- #ifdef A3000
- if (direction == DIRECTION_WRITE)
- *(ushort *)&board[0x42] = 0x38;
- else
- *(ushort *)&board[0x42] = 0x30;
-
- *(ulong *)&board[0x80] = 0;
- *(ulong *)&board[0x0c] = (ulong) address;
- *(ushort *)&board[0xe0] = 1;
- #endif
- }
-
- int
- read_scsi_reg (volatile ubyte *board, int reg_num)
- {
- int result;
-
- /* need to ensure, that register access is single threaded, the
- * board-interrupt will also try to use read_scsi_reg () .. */
- Disable();
- board[SCSI_CS_CNTL] = reg_num;
- result = board[SCSI_CS_DATA];
- Enable();
- return result;
- }
-
- static void inline
- read_scsi_regs (volatile ubyte *board, int reg_start, ubyte num, ubyte *buf)
- {
- /* need to ensure, that register access is single threaded, the
- * board-interrupt will also try to read those registers */
- Disable();
- board[SCSI_CS_CNTL] = reg_start;
- while (num --) *buf++ = board[SCSI_CS_DATA];
- Enable();
- }
-
- static void inline
- write_scsi_regs (volatile ubyte *board, int reg_start, ubyte num, ubyte *buf)
- {
- /* need to ensure, that register access is single threaded, the
- * board-interrupt will also try to read those registers */
- Disable();
- board[SCSI_CS_CNTL] = reg_start;
- while (num --) board[SCSI_CS_DATA] = *buf++;
- Enable();
- }
-
- static void
- write_scsi_reg (volatile ubyte *board, int reg_num, int value)
- {
- Disable();
- board[SCSI_CS_CNTL] = reg_num;
- board[SCSI_CS_DATA] = value;
- Enable();
- }
-
- static int
- checkfifo_n_dmareset (volatile ubyte *board)
- {
- #ifdef A2090
- ubyte i;
- int status;
-
- board[SCSI_PCSS] = DMA_READ_STATUS;
- for (i = 0; i <= 10; i++) ;
- status = board[SCSI_PCSD];
- for (i = 0; i <= 10; i++) ;
- board[SCSI_PCSS] = DMA_RESET_1;
- for (i = 0; i <= 10; i++) ;
- board[SCSI_PCSS] = DMA_RESET_2;
- for (i = 0; i <= 10; i++) ;
-
- return status & 0x20;
- #endif
- #ifdef A2091
- *(ushort *)&board[0xe8] = 1;
- while (!(board[0x41] & 1)) ;
- *(ushort *)&board[0xe4] = 1;
- *(ushort *)&board[0xe2] = 1;
- return 1; /* there seem to be no possible DMA errors.. GREAT!! */
- #endif
- }
-
- #ifdef A2090
- static void
- set_dma_state (volatile ubyte *board, int state, int addr)
- {
- ubyte i;
-
- board[SCSI_PCSS] = state;
- /* busy loop */
- for (i = 0; i <= 10; i++) ;
-
- board[SCSI_PCSD] = addr;
- /* busy loop */
- for (i = 0; i <= 10; i++) ;
- }
- #endif
-
- /***********************************************************************/
- /***********************************************************************/
-
- #ifdef NEED_DUMP_REGS
- void
- dump_regs(char *title)
- {
- ubyte *board = get_board_address((int *)board);
-
- printf("%s: tgt-lun = $%2x, cmd-ph = $%2x, sync = $%2x,\nsrc-id = $%2x, stat = $%x, data = $%2x, aux = $%2x\n",
- title,
- read_scsi_reg(board, WD_TARGET_LUN),
- read_scsi_reg(board, WD_COMMAND_PHASE),
- read_scsi_reg(board, WD_SYNCHRONOUS_TRANSFER),
- read_scsi_reg(board, WD_SOURCE_ID),
- read_scsi_reg(board, WD_SCSI_STATUS),
- read_scsi_reg(board, WD_DATA),
- read_scsi_reg(board, WD_AUXILIARY_STATUS));
- /* wait for middle mouse button to be released */
- while (!(*(unsigned short *)0xdff016 & (1 << 8))) ;
- }
-
- void
- init_controller (ubyte *board)
- {
- }
- #endif
-